API Design & Development

Master modern API development: RESTful design patterns, GraphQL implementation, authentication strategies, testing methodologies, documentation, and performance optimization techniques.

RESTGraphQLOAuthTestingDocumentationPerformance

1. API Design Principles

Foundational concepts for creating maintainable, scalable, and developer-friendly APIs.

Design-First Approach

Start with API specification before implementation to ensure consistency and enable parallel development.

  • Define API contract using OpenAPI/Swagger
  • Validate design with stakeholders early
  • Generate client SDKs and server stubs
  • Mock APIs for frontend development
  • Version control API specifications
Strategy

Resource-Oriented Design

Model APIs around resources (nouns) rather than actions (verbs) for intuitive and predictable interfaces.

  • Use clear, consistent resource names
  • Implement standard CRUD operations
  • Support resource hierarchies and relationships
  • Use appropriate HTTP methods
  • Design for idempotency where applicable
Resources

Consistency & Standards

Establish and follow conventions across all API endpoints for predictable developer experience.

  • Consistent naming conventions (camelCase/snake_case)
  • Standard error response formats
  • Uniform pagination patterns
  • Common filtering and sorting parameters
  • Standardized date/time formats (ISO 8601)
Standards

Versioning Strategy

Plan for API evolution without breaking existing clients through thoughtful versioning approaches.

  • URL versioning: /v1/users, /v2/users
  • Header versioning: Accept: application/vnd.api+json;version=1
  • Semantic versioning for breaking changes
  • Backward compatibility considerations
  • Deprecation policies and migration paths
Versioning

Error Handling

Provide clear, actionable error messages with appropriate HTTP status codes and consistent structure.

  • Use appropriate HTTP status codes
  • Structured error response format
  • Human-readable error messages
  • Error codes for programmatic handling
  • Validation error details
Errors

Performance Considerations

Design APIs with performance in mind: efficient data transfer, caching, and scalability patterns.

  • Implement pagination for large datasets
  • Support field selection (sparse fieldsets)
  • Use compression (gzip, brotli)
  • HTTP caching headers and ETags
  • Rate limiting and throttling
Performance

HTTP Status Code Reference

200
OK - Success
201
Created
204
No Content
301
Moved Permanently
304
Not Modified
400
Bad Request
401
Unauthorized
403
Forbidden
404
Not Found
422
Unprocessable Entity
429
Too Many Requests
500
Internal Server Error

2. RESTful API Development

Deep dive into REST architecture principles and implementation best practices.

REST Constraints

  • Client-Server: Separation of concerns
  • Stateless: Each request contains all needed info
  • Cacheable: Responses explicitly cacheable or not
  • Uniform Interface: Consistent interaction patterns
  • Layered System: Hierarchical architecture
  • Code on Demand: Optional client-side execution

HTTP Methods Usage

  • GET: Retrieve resource(s), idempotent, cacheable
  • POST: Create resource, non-idempotent
  • PUT: Create/replace resource, idempotent
  • PATCH: Partial update, not necessarily idempotent
  • DELETE: Remove resource, idempotent
  • HEAD: GET without response body
  • OPTIONS: Discover allowed methods

RESTful API Example

// User Management API

// Get all users (with pagination)
GET /api/v1/users?page=1&limit=20&sort=created_at&order=desc
Response: 200 OK
{
  "data": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "email": "user@example.com",
      "name": "John Doe",
      "created_at": "2023-10-01T12:00:00Z",
      "updated_at": "2023-10-15T14:30:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 20,
    "total_pages": 5,
    "total_count": 100
  },
  "links": {
    "next": "/api/v1/users?page=2&limit=20",
    "last": "/api/v1/users?page=5&limit=20"
  }
}

// Get specific user
GET /api/v1/users/123e4567-e89b-12d3-a456-426614174000
Response: 200 OK
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "email": "user@example.com",
    "name": "John Doe",
    "profile": {
      "bio": "Software Developer",
      "avatar_url": "https://example.com/avatars/123.jpg"
    },
    "created_at": "2023-10-01T12:00:00Z",
    "updated_at": "2023-10-15T14:30:00Z"
  }
}

// Create new user
POST /api/v1/users
Content-Type: application/json
{
  "email": "newuser@example.com",
  "name": "Jane Smith",
  "password": "securePassword123!"
}
Response: 201 Created
Location: /api/v1/users/456e7890-e89b-12d3-a456-426614174001

// Update user
PATCH /api/v1/users/123e4567-e89b-12d3-a456-426614174000
Content-Type: application/json
{
  "name": "John Smith"
}
Response: 200 OK

// Delete user
DELETE /api/v1/users/123e4567-e89b-12d3-a456-426614174000
Response: 204 No Content

// Error response example
GET /api/v1/users/invalid-id
Response: 400 Bad Request
{
  "error": {
    "code": "INVALID_USER_ID",
    "message": "User ID must be a valid UUID",
    "details": {
      "field": "id",
      "provided": "invalid-id"
    }
  }
}

Advanced REST Patterns

HATEOAS

Hypermedia as the Engine of Application State - Include navigation links in responses.

  • Self-descriptive messages
  • Discoverable API navigation
  • Reduced client-server coupling
  • Dynamic relationship exploration

Content Negotiation

Support multiple representation formats based on client preferences.

  • Accept header: application/json, text/xml
  • Accept-Language: en-US, es-ES
  • Accept-Encoding: gzip, deflate, br
  • Custom media types for versioning

Caching Strategies

Implement HTTP caching for improved performance and reduced server load.

  • Cache-Control headers
  • ETags for conditional requests
  • Last-Modified timestamps
  • Vary header for content negotiation

3. GraphQL API Development

Modern API approach with flexible queries, strong typing, and efficient data fetching.

GraphQL Advantages

  • Single endpoint for all operations
  • Client-specified data requirements
  • Strong type system with schema
  • Real-time subscriptions
  • Introspection and tooling
  • Reduced over-fetching/under-fetching

Core Concepts

  • Schema: Type definitions and operations
  • Queries: Read operations
  • Mutations: Write operations
  • Subscriptions: Real-time updates
  • Resolvers: Data fetching functions
  • Context: Shared request information

GraphQL Schema Example

# GraphQL Schema Definition
type User {
  id: ID!
  email: String!
  name: String!
  posts: [Post!]!
  createdAt: DateTime!
  updatedAt: DateTime!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  comments: [Comment!]!
  publishedAt: DateTime
  createdAt: DateTime!
  updatedAt: DateTime!
}

type Comment {
  id: ID!
  content: String!
  author: User!
  post: Post!
  createdAt: DateTime!
}

type Query {
  user(id: ID!): User
  users(first: Int, after: String): UserConnection
  post(id: ID!): Post
  posts(
    first: Int
    after: String
    filter: PostFilter
    orderBy: PostOrderBy
  ): PostConnection
}

type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload!
  updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
  deleteUser(id: ID!): DeleteUserPayload!
  
  createPost(input: CreatePostInput!): CreatePostPayload!
  publishPost(id: ID!): PublishPostPayload!
  addComment(input: AddCommentInput!): AddCommentPayload!
}

type Subscription {
  postAdded(authorId: ID): Post
  commentAdded(postId: ID!): Comment
}

input CreateUserInput {
  email: String!
  name: String!
  password: String!
}

input PostFilter {
  authorId: ID
  published: Boolean
  search: String
}

enum PostOrderBy {
  CREATED_AT_ASC
  CREATED_AT_DESC
  TITLE_ASC
  TITLE_DESC
}

# Pagination types
type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  node: User!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

GraphQL Query Examples

# Fetch user with posts and comments
query GetUserWithPosts($userId: ID!) {
  user(id: $userId) {
    id
    name
    email
    posts(first: 10) {
      edges {
        node {
          id
          title
          publishedAt
          comments(first: 5) {
            edges {
              node {
                id
                content
                author {
                  name
                }
                createdAt
              }
            }
          }
        }
      }
    }
  }
}

# Create new post mutation
mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    post {
      id
      title
      content
      author {
        name
      }
      createdAt
    }
    errors {
      field
      message
    }
  }
}

# Subscribe to new comments
subscription CommentAdded($postId: ID!) {
  commentAdded(postId: $postId) {
    id
    content
    author {
      name
    }
    createdAt
  }
}

GraphQL Best Practices

N+1 Problem Solutions

  • DataLoader for batching
  • Query complexity analysis
  • Depth limiting
  • Query allowlisting

Security Considerations

  • Query depth limiting
  • Query complexity scoring
  • Rate limiting per user/IP
  • Field-level authorization

Performance Optimization

  • Persistent queries
  • Automatic persisted queries (APQ)
  • Response caching
  • Federation for microservices

4. Authentication & Authorization

Secure API access with modern authentication patterns and authorization strategies.

Authentication Methods

  • JWT: Stateless tokens with claims
  • OAuth 2.0: Delegated authorization
  • API Keys: Simple service-to-service auth
  • Session Cookies: Traditional web auth
  • SAML: Enterprise single sign-on
  • OpenID Connect: Identity layer over OAuth

Authorization Patterns

  • RBAC: Role-based access control
  • ABAC: Attribute-based access control
  • ACL: Access control lists
  • Policy-based: Rule-driven decisions
  • Resource-based: Object-level permissions
  • Hierarchical: Inherited permissions

JWT Authentication Implementation

# JWT Token Structure
# Header.Payload.Signature

# Header
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-id-1"
}

# Payload (Claims)
{
  "sub": "user-123",           # Subject (user ID)
  "iss": "https://api.example.com",  # Issuer
  "aud": "mobile-app",         # Audience
  "exp": 1698765432,           # Expiration timestamp
  "iat": 1698661432,           # Issued at timestamp
  "jti": "token-uuid",         # JWT ID (for revocation)
  "scope": "read write",       # Permissions
  "roles": ["user", "premium"], # User roles
  "email": "user@example.com"  # Custom claims
}

# Authentication Flow
POST /api/v1/auth/login
{
  "email": "user@example.com",
  "password": "securePassword123!"
}

Response: 200 OK
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_token_value",
  "scope": "read write"
}

# Using JWT in requests
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

# Token refresh
POST /api/v1/auth/refresh
{
  "refresh_token": "refresh_token_value"
}

Response: 200 OK
{
  "access_token": "new_access_token",
  "expires_in": 3600
}

OAuth 2.0 Flows

Authorization Code Flow

Most secure flow for web applications with server-side component.

  • User redirected to authorization server
  • Authorization code exchanged for tokens
  • Supports PKCE for additional security
  • Refresh tokens for long-lived access
Web Apps

Client Credentials Flow

Server-to-server authentication without user involvement.

  • Direct token exchange with credentials
  • Machine-to-machine communication
  • API service authentication
  • No refresh tokens needed
M2M

Device Code Flow

Authentication for devices without web browsers or limited input.

  • Device displays user code
  • User authorizes on separate device
  • Polling for authorization status
  • Smart TVs, IoT devices, CLIs
IoT

5. API Testing Strategies

Comprehensive testing approaches for reliable and robust API implementations.

Testing Pyramid

  • Unit Tests: Individual function testing
  • Integration Tests: Component interaction
  • Contract Tests: API specification compliance
  • End-to-End Tests: Full workflow testing
  • Performance Tests: Load and stress testing
  • Security Tests: Vulnerability scanning

Testing Tools

  • Postman: Manual and automated testing
  • Insomnia: REST and GraphQL client
  • Jest/Mocha: JavaScript testing frameworks
  • Pact: Consumer-driven contract testing
  • Artillery: Performance testing
  • Newman: Postman CLI runner

Test Examples

// Unit Test Example (Jest)
describe('User API', () => {
  describe('POST /users', () => {
    it('should create user with valid data', async () => {
      const userData = {
        email: 'test@example.com',
        name: 'Test User',
        password: 'SecurePass123!'
      };
      
      const response = await request(app)
        .post('/api/v1/users')
        .send(userData)
        .expect(201);
        
      expect(response.body.data).toMatchObject({
        email: userData.email,
        name: userData.name
      });
      expect(response.body.data.id).toBeDefined();
      expect(response.body.data.password).toBeUndefined();
    });
    
    it('should return 400 for invalid email', async () => {
      const userData = {
        email: 'invalid-email',
        name: 'Test User',
        password: 'SecurePass123!'
      };
      
      const response = await request(app)
        .post('/api/v1/users')
        .send(userData)
        .expect(400);
        
      expect(response.body.error.code).toBe('INVALID_EMAIL');
    });
  });
});

// Integration Test Example
describe('User Integration Tests', () => {
  beforeEach(async () => {
    await clearDatabase();
    await seedTestData();
  });
  
  it('should handle complete user lifecycle', async () => {
    // Create user
    const createResponse = await request(app)
      .post('/api/v1/users')
      .send(testUserData)
      .expect(201);
      
    const userId = createResponse.body.data.id;
    
    // Get user
    await request(app)
      .get(`/api/v1/users/${userId}`)
      .expect(200);
      
    // Update user
    await request(app)
      .patch(`/api/v1/users/${userId}`)
      .send({ name: 'Updated Name' })
      .expect(200);
      
    // Delete user
    await request(app)
      .delete(`/api/v1/users/${userId}`)
      .expect(204);
      
    // Verify deletion
    await request(app)
      .get(`/api/v1/users/${userId}`)
      .expect(404);
  });
});

# Performance Test Example (Artillery)
config:
  target: 'https://api.example.com'
  phases:
    - duration: 60
      arrivalRate: 10
    - duration: 120
      arrivalRate: 50
    - duration: 60
      arrivalRate: 100
  
scenarios:
  - name: 'User API Load Test'
    requests:
      - get:
          url: '/api/v1/users'
          headers:
            Authorization: 'Bearer {{ $randomString() }}'
      - post:
          url: '/api/v1/users'
          json:
            email: '{{ $randomString() }}@example.com'
            name: '{{ $randomString() }}'

6. API Documentation & Developer Experience

Create comprehensive documentation that enables developer success and API adoption.

Documentation Types

  • API Reference: Endpoint specifications
  • Getting Started: Quick setup guides
  • Tutorials: Step-by-step walkthroughs
  • Code Examples: Language-specific samples
  • SDKs: Generated client libraries
  • Postman Collections: Interactive examples

Documentation Tools

  • OpenAPI/Swagger: Specification standard
  • Redoc: Beautiful API documentation
  • Stoplight: Design-first API platform
  • GitBook: Collaborative documentation
  • Postman: Collection documentation
  • Insomnia: Design and documentation

OpenAPI Specification Example

openapi: 3.0.3
info:
  title: User Management API
  description: A comprehensive API for user management operations
  version: 1.0.0
  contact:
    name: API Support
    email: api-support@example.com
    url: https://example.com/support
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://api.example.com/v1
    description: Production server
  - url: https://staging-api.example.com/v1
    description: Staging server

security:
  - bearerAuth: []
  - apiKey: []

paths:
  /users:
    get:
      summary: List users
      description: Retrieve a paginated list of users with optional filtering
      operationId: getUsers
      tags:
        - Users
      parameters:
        - name: page
          in: query
          description: Page number for pagination
          required: false
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: limit
          in: query
          description: Number of users per page
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: search
          in: query
          description: Search term for filtering users
          required: false
          schema:
            type: string
            maxLength: 100
      responses:
        '200':
          description: Users retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsersResponse'
              examples:
                success:
                  summary: Successful response
                  value:
                    data:
                      - id: "123e4567-e89b-12d3-a456-426614174000"
                        email: "user@example.com"
                        name: "John Doe"
                        created_at: "2023-10-01T12:00:00Z"
                    pagination:
                      current_page: 1
                      per_page: 20
                      total_pages: 5
                      total_count: 100
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

    post:
      summary: Create user
      description: Create a new user account
      operationId: createUser
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
            examples:
              valid_user:
                summary: Valid user creation
                value:
                  email: "newuser@example.com"
                  name: "Jane Smith"
                  password: "SecurePass123!"
      responses:
        '201':
          description: User created successfully
          headers:
            Location:
              description: URL of the created user
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          description: User already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - name
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
          description: Unique user identifier
          example: "123e4567-e89b-12d3-a456-426614174000"
        email:
          type: string
          format: email
          description: User's email address
          example: "user@example.com"
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User's full name
          example: "John Doe"
        created_at:
          type: string
          format: date-time
          description: User creation timestamp
          example: "2023-10-01T12:00:00Z"
        updated_at:
          type: string
          format: date-time
          description: User last update timestamp
          example: "2023-10-15T14:30:00Z"

    CreateUserRequest:
      type: object
      required:
        - email
        - name
        - password
      properties:
        email:
          type: string
          format: email
          description: User's email address
          example: "user@example.com"
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User's full name
          example: "John Doe"
        password:
          type: string
          minLength: 8
          maxLength: 128
          description: User's password (minimum 8 characters)
          example: "SecurePass123!"

  responses:
    BadRequest:
      description: Bad request - Invalid input data
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    Unauthorized:
      description: Unauthorized - Invalid or missing authentication
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key

7. Interactive API Labs

Practice API development concepts with hands-on tools and simulators.

OpenAPI Generator

(Generated OpenAPI specification will appear here)

JWT Token Decoder

(Decoded JWT payload will appear here)

API Response Generator

(Generated API response will appear here)

Rate Limiting Calculator

(Rate limiting analysis will appear here)

8. API Mastery Review

Comprehensive assessment of API development skills and best practices.

Mastery Checklist

Expert Q&A

When should you choose GraphQL over REST?

Choose GraphQL when you need flexible data fetching, have multiple client types with different data needs, want to reduce over-fetching, need real-time subscriptions, or work with complex, interconnected data. REST is better for simpler CRUD operations, caching requirements, or when you need mature tooling ecosystem.

How do you handle API versioning without breaking clients?

Use semantic versioning, maintain backward compatibility for minor versions, provide clear deprecation timelines, support multiple API versions simultaneously, use content negotiation for version selection, and implement gradual migration strategies with proper documentation.

What are the key considerations for API rate limiting?

Consider business requirements, user tiers, endpoint sensitivity, burst vs sustained load, fair usage policies, rate limit algorithms (token bucket, sliding window), proper error responses (429 status), and rate limit headers for client guidance.

How do you secure APIs against common vulnerabilities?

Implement proper authentication/authorization, validate all inputs, use HTTPS everywhere, protect against injection attacks, implement rate limiting, use CORS correctly, validate JWT tokens properly, sanitize outputs, and regularly update dependencies.

What strategies improve API performance at scale?

Implement caching (HTTP, Redis, CDN), optimize database queries, use pagination and filtering, implement compression, use async processing for heavy operations, implement circuit breakers, monitor performance metrics, and scale horizontally with load balancing.